﻿using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

namespace DotNetCommon;

/// <summary>
/// 代理流, 解决的场景: 防止传递给reader的stream在reader关闭的时候也自动关闭掉, 比如:<br /><br />
/// 用户上传了一张图片, 后台使用 IFormFile.OpenReadStream() 获取流后交给 SkiaSharp.SKBitmap.Decode() 去处理(比如: 缩放生成缩略图)<br />
/// 但是当 SkiaSharp 处理完成后却将这个流自动关掉了, 可能后面还有操作要处理这个流, 不想让它关掉. 示例:
/// <code>
/// Stream sourceStream = ...;
/// var agentStream = new AgentStream(sourceStream)
/// using (var bitMap = SKBitmap.Decode(agentStream))
/// {
///     //缩放生成缩略图
/// } //bitMap释放的时候会将 agentStream 释放, 但 agentStream 释放的时候不会将 sourceStream 释放
/// //sourceStream在这里仍然是可用的
/// </code>
/// </summary>
public class AgentStream : Stream
{
    private readonly Stream _sourceStream;
    private long _sourceStreamPosition;
    private bool _keepPosition;

    public AgentStream(Stream sourceStream, bool keepPosition = false)
    {
        AssertUtil.NotNull(sourceStream);
        _sourceStream = sourceStream;
        _sourceStreamPosition = sourceStream.Position;
        _keepPosition = keepPosition;
        if (keepPosition && !sourceStream.CanSeek) throw new Exception($"当前流不允许 Seek, 所以无法保证在 AgentStream dispose 后, 能将 position 移动到原位置.");
    }

    public override bool CanRead => _sourceStream.CanRead;
    public override bool CanSeek => _sourceStream.CanSeek;
    public override bool CanWrite => _sourceStream.CanWrite;
    public override long Length => _sourceStream.Length;

    public override long Position
    {
        get => _sourceStream.Position;
        set => _sourceStream.Position = value;
    }

    public override void Flush()
    {
        _sourceStream.Flush();
    }

    public override int Read(byte[] buffer, int offset, int count)
    {
        return _sourceStream.Read(buffer, offset, count);
    }

    public override long Seek(long offset, SeekOrigin origin)
    {
        return _sourceStream.Seek(offset, origin);
    }

    public override void SetLength(long value)
    {
        _sourceStream.SetLength(value);
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
        _sourceStream.Write(buffer, offset, count);
    }

    protected override void Dispose(bool disposing)
    {
        // 不关闭源Stream
        base.Dispose(disposing);
        if (_keepPosition) _sourceStream.Position = _sourceStreamPosition;
    }

    public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
    {
        return await _sourceStream.ReadAsync(buffer, offset, count, cancellationToken);
    }

    public override async ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
    {
        return await _sourceStream.ReadAsync(buffer, cancellationToken);
    }

    public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
    {
        await _sourceStream.WriteAsync(buffer, offset, count, cancellationToken);
    }

    public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
    {
        return _sourceStream.WriteAsync(buffer, cancellationToken);
    }
}